import { Inject, Injectable, InjectFactory, TFactory } from '../decorators/inject';
import { NExcel } from '../types';
import { Config } from './config';
import { TableDatas } from './table-datas';
import { DrawHelper } from '../utils/draw-helper';
import { TableHelper } from '../utils/table-helper';
import { Hook } from './hook';
import { Cache } from './cache';
import { FreezeRender } from './freeze-render';
import { AddHook } from '../decorators/add-hook';
import { clip } from '../utils/clip';
@Injectable('ColHeader')
export class ColHeader {
    protected ctx: CanvasRenderingContext2D;
    protected colWidths: number[] = [];
    protected startCol: number = null;
    protected selectedColidxs: number[] = [];
    protected drawLine: (x: number) => void = () => {}
    protected drawActiveLine: (x1: number, x2: number) => void = () => {}
    constructor(
        @Inject(NExcel.CanvasToken) protected canvasEle: HTMLCanvasElement,
        protected conf: Config,
        protected hooks: Hook,
        protected datas: TableDatas,
        protected cache: Cache,
        protected freezeRender: FreezeRender,
        @InjectFactory() public factory: TFactory
        ) {
    }
    public init() {
        this.ctx = this.canvasEle.getContext('2d');
        this.updateDrawLine();
        this.hooks.afterSelect.add((range) => {
            this.selectedColidxs = [];
            if(range == null) {
                this.render();
                return;
            }
            for(let i = 0; i < range.colspan; i++) {
                this.selectedColidxs.push(i+range.col);
            }
            this.render();
        });
    }
    @AddHook('afterScaleChange')
    @AddHook('pixelRatioChange')
    protected updateDrawLine() {
        const lineWidth = 1;
        this.drawLine = (() => {
            let drawLine = DrawHelper.line(this.ctx)({width: lineWidth, type: 'solid', color: '#ccc'});
            return (x: number) => {
                drawLine({ x: x, y: 0 }, { x: x, y: this.conf.header.col.height });
            }
        })();
        this.drawActiveLine = (() => {
            let drawActiveLine = DrawHelper.line(this.ctx)({width: lineWidth, type: 'solid', color: '#0188fb'});
            return (x1: number, x2: number) => {
                drawActiveLine(
                    {
                        x: x1, 
                        y: this.conf.header.col.height - 2
                    },
                    {
                        x: x2,
                        y: this.conf.header.col.height - 2
                    }
                );
            }
        })();
    }
    public getWidth(col: number) {
        if(this.colWidths[col] == undefined) {
            return this.conf.header.col.width;
        }
        return this.colWidths[col] * this.conf.enlarge;
    }
    public getStartCol(): number {
        return this.startCol;
    }
    protected drawBg() {
        this.ctx.save();
        this.ctx.beginPath();
        this.ctx.fillStyle = '#f0f0f0';
        this.ctx.fillRect(this.conf.header.row.width, 0, this.canvasEle.width, this.conf.header.col.height);
        this.ctx.stroke();
        this.ctx.restore();
    }
    protected drawHeaderCell() {
        let maxBreakCol = -1;
        const freezeWidth = this.freezeRender.getStartColWidth();
        let widthTotal = freezeWidth;
        let col = this.conf.freeze.col + 1;
        this.startCol = null;
        while(true) {
            if(maxBreakCol++ > this.conf.allowMaxRender.col) break;
            let start = { x: widthTotal  - this.conf.view.col};
            const colWidth = this.datas.getColWidth(col);
            if(start.x + colWidth <= freezeWidth) {
                widthTotal += colWidth;
                col++;
                continue;
            }
            if(widthTotal - this.conf.view.col - this.conf.header.row.width > this.canvasEle.width) {
                this.cache.setColBoundary(this.startCol, col - 1);
                break;
            }
            if(widthTotal <= this.conf.view.col + this.canvasEle.width - this.conf.header.row.width) {
                if(this.startCol == null) {
                    this.startCol = col;
                }
                widthTotal += colWidth;
                let x = widthTotal - this.conf.view.col + this.conf.header.row.width;
                let start = { x: x, y: 0 };
                if(this.selectedColidxs.indexOf(col) != -1) {
                    const x = start.x - colWidth;
                    const w = colWidth;
                    if(col > this.conf.freeze.col) {
                        const startX = this.freezeRender.getStartColWidth() + this.conf.header.row.width;
                        if(x < startX) {
                            clip(this.ctx, () => {
                                this.ctx.rect(startX + 1, 0, x + w - startX, this.conf.header.col.height);
                            }, () => {
                                this.drawActiveCol(x, w);
                            });
                        } else {
                            this.drawActiveCol(x, w);
                        }
                    } else {
                        this.drawActiveCol(x, w);
                    }
                }
                this.drawLine(x);
                this.cache.setColPosition(col, start.x - colWidth, start.x);
                if(col > this.conf.freeze.col && start.x - colWidth < freezeWidth + this.conf.header.row.width) {
                    this.cache.setColClipPosition(col, freezeWidth + this.conf.header.row.width, start.x);
                }
                this.drawCell(col, x - (colWidth) / 2);
                col++;
            } else {
                this.cache.setColBoundary(this.startCol, col - 1);
                break;
            }
        }
    }
    protected drawActiveCol(x: number, w: number) {
        this.ctx.save();
        this.ctx.beginPath();
        this.ctx.fillStyle="#dddddd";
        this.ctx.fillRect(x, 0,w,this.conf.header.col.height - 2);
        this.ctx.stroke();
        this.ctx.restore();
        this.drawActiveLine(x, x + w);
    }
    protected drawCell(col: number, x: number) {
        let freezeCol = this.conf.freeze.col;
        const header = this.conf.header;
        const draw = () => {
            this.ctx.save();
            this.ctx.beginPath();
            this.ctx.textAlign = 'center';
            this.ctx.textBaseline = 'middle';
            this.ctx.font = `${10*this.conf.enlarge}px '微软雅黑','宋体'`;
            let colName = TableHelper.mapNumToCol(col + 1);
            if(__ENV__ === 'development') {
                colName = colName + col;
            }
            if(!this.conf.hiddenCols.has(col)) {
                this.ctx.fillText(colName, x, (this.conf.header.col.height) / 2);
            }
            this.ctx.stroke();
            this.ctx.restore();
        }
        const freezeWidth = this.freezeRender.getStartColWidth();
        const head = this.conf.header;
        if(col > freezeCol) {
            clip(this.ctx, () => {
                this.ctx.rect(head.row.width + freezeWidth, 0, this.canvasEle.clientWidth * this.conf.pixelRatio - head.row.width - freezeWidth, head.col.height)
            }, () => {
                draw();
            });
        } else {
            clip(this.ctx, () => {
                this.ctx.rect(head.row.width, 0, freezeWidth, head.col.height)
            }, () => {
                draw();
            })
        }
    }
    protected drawBottomLine() {
        let opt: any = {width: 1, color: '#ccc', type: 'solid'};
        let start = {x: 0, y: this.conf.header.col.height - 1};
        let end = {x: this.canvasEle.width, y: this.conf.header.col.height};
        DrawHelper.line(this.ctx)(opt)(start, end);
    }
    protected drawFreezeBg() {

    }
    protected drawFreezeRightLine() {

    }
    protected drawFreezeHeaderCell() {
        if(this.conf.freeze.col === -1) {
            return;
        }
        const freezeCol = this.conf.freeze.col + 1;
        let lastPosition = this.conf.header.row.width;
        for(let col = 0; col < freezeCol; col++) {
            let w = this.datas.getColWidth(col);
            let x = lastPosition;
            this.drawLine(x);
            lastPosition = x + w;
            this.drawCell(col, x + w / 2);
            this.cache.setColPosition(col, x, lastPosition);
        }
    }
    protected drawFreezeHeader() {
        this.drawFreezeBg();
        this.drawFreezeRightLine();
        this.drawFreezeHeaderCell();
    }
    protected storyFreezePostion() {
        let width = this.freezeRender.getStartColWidth();
        this.cache.updateFreezeColPosition(width);
    }
    public render() {
        this.hooks.beforeRenderColHeader.excute();
        this.storyFreezePostion();
        this.drawBg();
        this.drawBottomLine();
        this.drawFreezeHeader();
        this.drawHeaderCell();
        this.hooks.afterRenderColHeader.excute();
    }
}